package com.baidu.mpks.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;

public class Utils {

    public static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT_PATTERN_YYYY_MM_DD = "yyyy-MM-dd";
    private static final ZoneOffset DEFAULT_ZONE_OFFSET = ZoneOffset.of("+8");
    public static final String CHARSET_NAME = "UTF-8";
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT_PATTERN);
    public static final DateTimeFormatter DATE_TIME_FORMATTER_YYYY_MM_DD
            = DateTimeFormatter.ofPattern(DATE_FORMAT_PATTERN_YYYY_MM_DD);

    public static ObjectMapper objectMapper = SingletonMapper.getInstance();

    public static long convertDateToMilli(String date) {
        LocalDateTime localDateTime = LocalDateTime.parse(date, DATE_TIME_FORMATTER);
        return localDateTime.toInstant(DEFAULT_ZONE_OFFSET).toEpochMilli();
    }

    public static String convertMillisToDate(long ts) {
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(ts / 1000, 0, ZoneOffset.ofHours(8));
        return localDateTime.format(DATE_TIME_FORMATTER);
    }

    public static String parseDate(String date) {
        LocalDateTime localDateTime = LocalDateTime.parse(date, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
        return localDateTime.format(DATE_TIME_FORMATTER);
    }


    public static String convertMillisToDate2(long ts) {
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(ts / 1000, 0, ZoneOffset.ofHours(8));
        return localDateTime.format(DATE_TIME_FORMATTER_YYYY_MM_DD);
    }


    public static String getStringFromJsonNode(String key, JsonNode jsonNode) throws Exception {
        if (jsonNode.has(key)) {
            JsonNode stringNode = jsonNode.get(key);
            if (stringNode.isTextual()) {
                return stringNode.asText();
            } else {
                throw new Exception(key + " should be string");
            }
        } else {
            throw new Exception("please provide " + key);
        }
    }

    public static String getStringFromJsonNode(String key, JsonNode jsonNode, String defaultValue) throws Exception {
        if (jsonNode.has(key)) {
            JsonNode stringNode = jsonNode.get(key);
            if (stringNode.isTextual()) {
                return stringNode.asText();
            } else {
                throw new Exception(key + " should be string");
            }
        } else {
            return defaultValue;
        }
    }

    public static int getIntFromJsonNode(String key, JsonNode jsonNode) {
        if (jsonNode.has(key)) {
            return jsonNode.get(key).asInt();
        } else {
            return 0;
        }
    }

    public static int getIntFromJsonNode(String key, JsonNode jsonNode, int defaultValue) {
        if (jsonNode.has(key)) {
            return jsonNode.get(key).asInt();
        } else {
            return defaultValue;
        }
    }

    public static long getLongFromJsonNode(String key, JsonNode jsonNode) {
        if (jsonNode.has(key)) {
            return jsonNode.get(key).asLong();
        } else {
            return -1L;
        }
    }

    public static String md5(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(str.getBytes(CHARSET_NAME));

            final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
            StringBuilder ret = new StringBuilder(bytes.length * 2);
            for (int i = 0; i < bytes.length; i++) {
                ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
                ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
            }
            return ret.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String serialize(Object obj) throws IOException {
        String result;
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(obj);
            result = URLEncoder.encode(baos.toString("ISO-8859-1"), CHARSET_NAME);
        } catch (IOException e) {
            throw new IOException("serialize failed, err msg: " + e);
        }
        return result;
    }

    public static Object deserialize(String str) throws Exception {
        Object object = null;
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try {
            byte[] bytes = URLDecoder.decode(str, CHARSET_NAME).getBytes(StandardCharsets.ISO_8859_1);
            bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            object = ois.readObject();
        } catch (Exception e) {
            throw new Exception("deserialize failed, err msg: " + e);
        } finally {
            if (bais != null) {
                bais.close();
            }
            if (ois != null) {
                ois.close();
            }
        }

        return object;

    }

    /**
     *  convert {query=gta5} to {"query":"gt"}
     * @param map
     * @return
     */
    public static JsonNode parseMapToJson(String map) throws Exception{
        ObjectNode jsonNode = objectMapper.createObjectNode();
        try {
            String trimed = map.replace("{", "").replace("}", "");
            String[] trimedArr = trimed.split(",");
            Arrays.stream(trimedArr).forEach(e -> {
                String[] kv = e.trim().split("=");
                jsonNode.put(kv[0], kv[1]);
            });
        } catch (Exception e) {
            System.out.println("err string >>>>>>>>>>> " + map);
            throw new Exception("map string format may be wrong, err: " + e);
        }
        return jsonNode;
    }
}